﻿using System;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;

namespace Script.Utils
{
    [Serializable]
    public class PrefabPool
    {
        public Transform SpawnRoot;
        public Transform PoolRoot;

        public          GameObject          prefab;
        public readonly Stack<GameObject>   inPoolObjs  = new Stack<GameObject> ();
        public readonly HashSet<GameObject> outPoolObjs = new HashSet<GameObject> ();

        public void PreLoad (int count)
        {
            if (prefab == null)
                return;
            if (inPoolObjs.Count + outPoolObjs.Count >= count)
                return;

            var n = count - inPoolObjs.Count - outPoolObjs.Count;
            for (int i = 0; i < n; i++)
            {
                var obj = Object.Instantiate (prefab, Vector3.zero, Quaternion.identity);
                obj.SetActive (false);
                inPoolObjs.Push (obj);
            }
        }

        public GameObject Spawn ()
        {
            GameObject obj;
            if (inPoolObjs.Count > 0)
            {
                obj = inPoolObjs.Pop ();
                return obj;
            }
            else
            {
                obj = Object.Instantiate (prefab, Vector3.zero, Quaternion.identity);
            }
            outPoolObjs.Add (obj);
            obj.transform.SetParent (SpawnRoot);
            obj.SetActive (true);
            return obj;
        }

        public void DeSpawn (GameObject gameObject)
        {
            if (outPoolObjs.Contains (gameObject) == false)
            {
                Object.Destroy (gameObject);
            }
            else
            {
                outPoolObjs.Remove (gameObject);
                gameObject.SetActive (false);
                gameObject.transform.SetParent (PoolRoot);
                inPoolObjs.Push (gameObject);
            }
        }
    }

    [Serializable]
    public class PrefabPool<T> where T : Component
    {
        public Transform SpawnRoot;
        public Transform PoolRoot;

        public          GameObject prefab;
        public readonly Stack<T>   inPoolObjs  = new Stack<T> ();
        public readonly HashSet<T> outPoolObjs = new HashSet<T> ();

        public void PreLoad (int count)
        {
            if (prefab == null)
                return;
            if (inPoolObjs.Count + outPoolObjs.Count >= count)
                return;

            var n = count - inPoolObjs.Count - outPoolObjs.Count;
            for (int i = 0; i < n; i++)
            {
                var obj = Object.Instantiate (prefab, Vector3.zero, Quaternion.identity);
                obj.gameObject.SetActive (false);
                obj.transform.SetParent (PoolRoot);
                inPoolObjs.Push (obj.GetComponent<T> ());
            }
        }

        public T Spawn ()
        {
            T obj;
            if (inPoolObjs.Count > 0)
            {
                obj = inPoolObjs.Pop ();
            }
            else
            {
                obj = Object.Instantiate (prefab, Vector3.zero, Quaternion.identity).GetComponent<T> ();
            }
            outPoolObjs.Add (obj.GetComponent<T> ());
            obj.transform.SetParent (SpawnRoot);
            obj.gameObject.SetActive (true);
            return obj;
        }

        public void DeSpawn (T gameObject)
        {
            if (outPoolObjs.Contains (gameObject) == false)
            {
                Object.Destroy (gameObject);
            }
            else
            {
                outPoolObjs.Remove (gameObject);
                gameObject.gameObject.SetActive (false);
                gameObject.transform.SetParent (PoolRoot);
                inPoolObjs.Push (gameObject);
            }
        }
    }
}